package com.divillysausages.dsair.log 
{
	import com.divillysausages.dsair.DSAir;
	import flash.events.KeyboardEvent;
	import flash.ui.Keyboard;
	
	/**
	 * The LogManager that controls the logging for the engine. LogMsgs that are of level WARN
	 * or higher are always kept. Normal log messages are reused as needed
	 * @author Damian Connolly
	 */
	public class LogManager 
	{
		
		/********************************************************************/
		
		private static var m_instance:LogManager	= null;	// the only instance of the log manager
		private static var m_creating:Boolean		= false;// are we creating the log manager?
		
		/********************************************************************/
		
		/**
		 * The singleton instance for the log manager
		 */
		public static function get instance():LogManager
		{
			if ( LogManager.m_instance == null )
			{
				LogManager.m_creating = true;
				LogManager.m_instance = new LogManager;
				LogManager.m_creating = false;
			}
			return LogManager.m_instance;
		}
		
		/********************************************************************/
		
		/**
		 * The level for the logger. Increase the level to ignore messages under this
		 */
		public var level:LogLevel = LogLevel.DEBUG;
		
		/********************************************************************/
		
		private var m_allLogs:Vector.<LogMsg> 	= new Vector.<LogMsg>;	// all the logs we have
		private var m_reuseLogs:Vector.<LogMsg>	= new Vector.<LogMsg>;	// normal logs that we can reuse
		private var m_reuseLimit:int			= 10;	// how many normal logs to create before we reuse them
		private var m_display:LogDisplay		= null;	// the display to show our logs on the stage
		
		/********************************************************************/
		
		/**
		 * Is the logger visible on the stage?
		 */
		public function get visible():Boolean { return this.m_display.visible; }
		public function set visible( b:Boolean ):void
		{
			this.m_display.visible = b;
		}
		
		/********************************************************************/
		
		/**
		 * Creates the LogManager. As it's an instance, use the static instance getter instead
		 */
		public function LogManager() 
		{
			if ( !LogManager.m_creating )
				throw new Error( "The LogManager is a singleton. Use the static instance property instead" );
				
			// create our display
			this.m_display = new LogDisplay( this.m_allLogs );
			
			// the key to show/hide
			DSAir.bindKey( Keyboard.L, this._onToggleVisible );
			this.log( LogManager, "Press Ctrl+Shift+L to toggle the log display visibility" );
		}
		
		/**
		 * Logs a debug message
		 * @param reporter The object that's logging the message
		 * @param msg The message to log
		 */
		public function debug( reporter:*, msg:String ):void
		{
			this._log( reporter, msg, LogLevel.DEBUG );
		}
		
		/**
		 * Logs a basic message
		 * @param reporter The object that's logging the message
		 * @param msg The message to log
		 */
		public function log( reporter:*, msg:String ):void
		{
			this._log( reporter, msg, LogLevel.MSG );
		}
		
		/**
		 * Logs a warning message
		 * @param reporter The object that's logging the message
		 * @param msg The message to log
		 */
		public function warn( reporter:*, msg:String ):void
		{
			this._log( reporter, msg, LogLevel.WARN );
		}
		
		/**
		 * Logs an error message
		 * @param reporter The object that's logging the message
		 * @param msg The message to log
		 */
		public function error( reporter:*, msg:String ):void
		{
			this._log( reporter, msg, LogLevel.ERROR );
		}
		
		/********************************************************************/
		
		// logs a message to the output panel and stores an object so we can
		// also display it on a display
		private function _log( reporter:*, message:String, level:LogLevel ):void
		{
			// if it's not our level, ignore
			if ( level.index < this.level.index )
				return;
				
			// get a log message
			var log:LogMsg = this._getLog( level );
			
			// set our message and add it to our list
			log.init( reporter + " - " + message, level );
			this.m_allLogs.push( log );
			
			// trace it out (traceType will colour code it if you're in FlashDevelop
			var traceType:String = ( level == LogLevel.DEBUG ) ? "0:" : ( level == LogLevel.MSG ) ? "1:" : ( level == LogLevel.WARN ) ? "2:" : "3:";
			trace( traceType + log );
			
			// update the display if it's visible
			if ( this.m_display.visible )
				this.m_display.updateDisplay();
		}
		
		// returns a new log or reuses one if the type isn't too important
		private function _getLog( level:LogLevel ):LogMsg
		{
			var log:LogMsg = null;
			
			// if it's a normal log, check if we can reuse one
			if ( ( level == LogLevel.DEBUG || level == LogLevel.MSG ) && this.m_reuseLogs.length >= this.m_reuseLimit )
			{
				// take the oldest log message
				log = this.m_reuseLogs.shift();
				
				// remove the old one from our list
				var index:int = this.m_allLogs.indexOf( log );
				if ( index != -1 )
					this.m_allLogs.splice( index, 1 );
			}
			
			// create a new log message
			if( log == null )
				log = new LogMsg;
			
			// if it's a normal log, add it to our reuse array
			if ( level == LogLevel.DEBUG || level == LogLevel.MSG )
				this.m_reuseLogs.push( log );
				
			// return the log
			return log;
		}
		
		// shows/hides the logger
		private function _onToggleVisible( e:KeyboardEvent ):void
		{
			// to show/hide, it's ctrl+shift+l
			if ( !e.ctrlKey || !e.shiftKey )
				return;
			this.visible = !this.visible;
		}
		
	}

}